package com.ruoyi.framework.jwt.service;

import com.ruoyi.common.config.JwtConfig;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.jwt.JwtUtils;
import com.ruoyi.framework.jwt.domain.SysUserToken;
import com.ruoyi.framework.jwt.mapper.SysUserTokenMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * JWT Token服务类
 * 
 * @author ruoyi
 */
@Service
public class JwtTokenService
{
    private static final Logger log = LoggerFactory.getLogger(JwtTokenService.class);

    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private SysUserTokenMapper userTokenMapper;

    /**
     * 生成Token
     * 
     * @param user 用户信息
     * @param request 请求对象
     * @return Token信息
     */
    @Transactional
    public Map<String, Object> generateToken(SysUser user, HttpServletRequest request)
    {
        try
        {
            String tokenId = UUID.randomUUID().toString().replace("-", "");
            
            // 生成访问Token（使用指定的tokenId）
            String accessToken = JwtUtils.createAccessToken(
                tokenId,
                user.getUserId(), 
                user.getLoginName(), 
                jwtConfig.getSecret(), 
                jwtConfig.getAccessTokenExpireMillis()
            );
            
            // 生成刷新Token（使用指定的tokenId）
            String refreshToken = JwtUtils.createRefreshToken(
                tokenId,
                user.getUserId(), 
                user.getLoginName(), 
                jwtConfig.getSecret(),
                jwtConfig.getRefreshTokenExpireMillis()
            );
            
            // 保存Token信息到数据库
            SysUserToken userToken = new SysUserToken();
            userToken.setTokenId(tokenId);
            userToken.setUserId(user.getUserId());
            userToken.setAccessToken(accessToken);
            userToken.setRefreshToken(refreshToken);
            userToken.setAccessTokenExpire(new Date(System.currentTimeMillis() + jwtConfig.getAccessTokenExpireMillis()));
            userToken.setRefreshTokenExpire(new Date(System.currentTimeMillis() + jwtConfig.getRefreshTokenExpireMillis()));
            userToken.setDeviceType(getDeviceType(request));
            userToken.setDeviceId(getDeviceId(request));
            userToken.setIpaddr(getClientIP(request));
            userToken.setUserAgent(request.getHeader("User-Agent"));
            userToken.setLoginLocation(getLoginLocation(request));
            userToken.setStatus("0"); // 0-正常 1-禁用
            userToken.setCreateTime(DateUtils.getNowDate());
            userToken.setUpdateTime(DateUtils.getNowDate());
            
            // 删除该用户的旧Token（可选，根据业务需求决定是否允许多设备登录）
            // userTokenMapper.deleteByUserId(user.getUserId());
            
            userTokenMapper.insertUserToken(userToken);
            
            // 返回Token信息
            Map<String, Object> tokenInfo = new HashMap<>();
            tokenInfo.put("accessToken", accessToken);
            tokenInfo.put("refreshToken", refreshToken);
            tokenInfo.put("tokenType", "Bearer");
            tokenInfo.put("expiresIn", jwtConfig.getAccessTokenExpire() * 60); // 秒
            tokenInfo.put("refreshExpiresIn", jwtConfig.getRefreshTokenExpire() * 24 * 60 * 60); // 秒
            
            log.info("为用户 {} 生成Token成功，TokenId: {}", user.getLoginName(), tokenId);
            return tokenInfo;
        }
        catch (Exception e)
        {
            log.error("生成Token失败: {}", e.getMessage(), e);
            throw new RuntimeException("生成Token失败", e);
        }
    }

    /**
     * 刷新Token
     * 
     * @param refreshToken 刷新Token
     * @param request 请求对象
     * @return 新的Token信息
     */
    @Transactional
    public Map<String, Object> refreshToken(String refreshToken, HttpServletRequest request)
    {
        try
        {
            // 验证刷新Token
            if (!JwtUtils.validateToken(refreshToken, jwtConfig.getSecret()))
            {
                throw new RuntimeException("刷新Token无效或已过期");
            }
            
            String tokenId = JwtUtils.getTokenId(refreshToken, jwtConfig.getSecret());
            if (StringUtils.isEmpty(tokenId))
            {
                throw new RuntimeException("刷新Token中TokenId无效");
            }
            
            // 检查Token是否在黑名单中
            if (isTokenBlacklisted(tokenId))
            {
                throw new RuntimeException("刷新Token已被禁用");
            }
            
            // 查询Token信息
            SysUserToken userToken = userTokenMapper.selectByTokenId(tokenId);
            if (userToken == null || !"0".equals(userToken.getStatus()))
            {
                throw new RuntimeException("Token信息不存在或已被禁用");
            }
            
            // 生成新的访问Token（使用相同的tokenId）
            String newAccessToken = JwtUtils.createAccessToken(
                tokenId,
                userToken.getUserId(), 
                JwtUtils.getUsername(refreshToken, jwtConfig.getSecret()), 
                jwtConfig.getSecret(), 
                jwtConfig.getAccessTokenExpireMillis()
            );
            
            // 更新Token信息
            userToken.setAccessToken(newAccessToken);
            userToken.setAccessTokenExpire(new Date(System.currentTimeMillis() + jwtConfig.getAccessTokenExpireMillis()));
            userToken.setUpdateTime(DateUtils.getNowDate());
            userTokenMapper.updateUserToken(userToken);
            
            // 返回新的Token信息
            Map<String, Object> tokenInfo = new HashMap<>();
            tokenInfo.put("accessToken", newAccessToken);
            tokenInfo.put("refreshToken", refreshToken); // 刷新Token保持不变
            tokenInfo.put("tokenType", "Bearer");
            tokenInfo.put("expiresIn", jwtConfig.getAccessTokenExpire() * 60); // 秒
            
            log.info("刷新Token成功，TokenId: {}", tokenId);
            return tokenInfo;
        }
        catch (Exception e)
        {
            log.error("刷新Token失败: {}", e.getMessage(), e);
            throw new RuntimeException("刷新Token失败: " + e.getMessage(), e);
        }
    }

    /**
     * 注销Token
     * 
     * @param tokenId Token ID
     * @param reason 注销原因
     */
    @Transactional
    public void revokeToken(String tokenId, String reason)
    {
        try
        {
            // 查询Token信息
            SysUserToken userToken = userTokenMapper.selectByTokenId(tokenId);
            if (userToken != null)
            {
                // 更新Token状态为已注销
                userToken.setStatus("2"); // 2表示已注销
                userToken.setRevokeReason(reason);
                userToken.setUpdateTime(DateUtils.getNowDate());
                userTokenMapper.updateUserToken(userToken);
                
                log.info("注销Token成功，TokenId: {}, 原因: {}", tokenId, reason);
            }
        }
        catch (Exception e)
        {
            log.error("注销Token失败: {}", e.getMessage(), e);
            throw new RuntimeException("注销Token失败", e);
        }
    }

    /**
     * 检查Token是否被禁用
     * 
     * @param tokenId Token ID
     * @return 是否被禁用
     */
    public boolean isTokenBlacklisted(String tokenId)
    {
        try
        {
            SysUserToken userToken = userTokenMapper.selectByTokenId(tokenId);
            // status: 0正常 1停用 2已注销
            return userToken == null || !"0".equals(userToken.getStatus());
        }
        catch (Exception e)
        {
            log.error("检查Token状态失败: {}", e.getMessage(), e);
            return true; // 出错时认为Token无效
        }
    }



    /**
     * 获取设备类型
     */
    private String getDeviceType(HttpServletRequest request)
    {
        String userAgent = request.getHeader("User-Agent");
        if (StringUtils.isEmpty(userAgent))
        {
            return "unknown";
        }
        
        userAgent = userAgent.toLowerCase();
        if (userAgent.contains("mobile") || userAgent.contains("android") || userAgent.contains("iphone"))
        {
            return "mobile";
        }
        else if (userAgent.contains("tablet") || userAgent.contains("ipad"))
        {
            return "tablet";
        }
        else
        {
            return "desktop";
        }
    }

    /**
     * 获取设备ID
     */
    private String getDeviceId(HttpServletRequest request)
    {
        // 可以从请求头中获取设备唯一标识，这里简单使用User-Agent的hash值
        String userAgent = request.getHeader("User-Agent");
        return StringUtils.isEmpty(userAgent) ? "unknown" : String.valueOf(userAgent.hashCode());
    }

    /**
     * 获取客户端IP
     */
    private String getClientIP(HttpServletRequest request)
    {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 获取登录地点
     */
    private String getLoginLocation(HttpServletRequest request)
    {
        // 这里可以根据IP地址获取地理位置，简单返回IP
        return getClientIP(request);
    }
}